home *** CD-ROM | disk | FTP | other *** search
- /*
- * Copyright 1992, 1993, 1994, Silicon Graphics, Inc.
- * All Rights Reserved.
- *
- * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Silicon Graphics, Inc.;
- * the contents of this file may not be disclosed to third parties, copied or
- * duplicated in any form, in whole or in part, without the prior written
- * permission of Silicon Graphics, Inc.
- *
- * RESTRICTED RIGHTS LEGEND:
- * Use, duplication or disclosure by the Government is subject to restrictions
- * as set forth in subdivision (c)(1)(ii) of the Rights in Technical Data
- * and Computer Software clause at DFARS 252.227-7013, and/or in similar or
- * successor clauses in the FAR, DOD or NASA FAR Supplement. Unpublished -
- * rights reserved under the Copyright Laws of the United States.
- */
- /***********************************************************************
- *
- * someWaves.c: code to generate a bunch of oscillators, waveforms,
- * change playback rate, mix them and send them to speaker.
- *
- * SpecifyLoopType: set loop to off, forward, backward or forward/backward
- * ResetOscillator: reset oscillator to play from beginning.
- * InitAL: create,initialize an AL output port.
- * CreateOscillator: create and initialize oscillator data structure
- * RunOscillator: play data with loops.
- * MixOutputBuffers: mix specified number of output buffers in equal
- * proportion.
- * SetPlaybackRatio: specify ratio: playback rate/original sampling rate
- * SetPlaybackEqualTemperedRatio: specify ratio: playback rate/original sampling
- * rate in equal-tempered units of half steps and cents.
- * GenerateSawtoothWave: generate non-bandlimited sawtooth wave.
- * GenerateTriangleWave: generate non-bandlimited triangle wave.
- * GeneratePulseWave: generate non-bandlimited pulse wave of specified duty cycle.
- * GenerateWave: generate general wave as sum of of cosines of specified
- * length.
- * InterleaveShorts: interleave two buffers (of equal length) of shorts
- * into a single buffer of shorts.
- * DestroyOscillator: destroy oscillator data structure
- *
- * Written by Gints Klimanis
- * Silicon Graphics Computer Systems
- * 1992
- * gints@roadkill.esd.sgi.com
- *
- * This code is public domain. I am not responsible for inaccuracies
- * in code, comments or theory. This code was written in early
- * morning hours.
- *
- *
- Makefile
- SHELL = /bin/sh
- CFLAGS = -O2 -cckr -float
- ALL= someSawtoothWaves someSquareWaves somePulseWaves somePulseWidthModulatedWaves someCoolWaves
-
- all: $(ALL)
-
- someSawtoothWaves: someWaves.o
- $(CC) -DSAWTOOTH_WAVE $(CFLAGS) -c someWaves.c
- $(CC) -o $@ someWaves.o -laudio -laudiofile -lm -lmalloc
-
- someSquareWaves: someWaves.o
- $(CC) -DSQUARE_WAVE $(CFLAGS) -c someWaves.c
- $(CC) -o $@ someWaves.o -laudio -laudiofile -lm -lmalloc
-
- somePulseWaves: someWaves.o
- $(CC) -DPULSE_WAVE $(CFLAGS) -c someWaves.c
- $(CC) -o $@ someWaves.o -laudio -laudiofile -lm -lmalloc
-
- somePulseWidthModulatedWaves: someWaves.o
- $(CC) -DPULSE_WAVE -DPULSE_WIDTH_MODULATION $(CFLAGS) -c someWaves.c
- $(CC) -o $@ someWaves.o -laudio -laudiofile -lm -lmalloc
-
- someCoolWaves: someWaves.o
- $(CC) -DCOOL_WAVE $(CFLAGS) -c someWaves.c
- $(CC) -o $@ someWaves.o -laudio -laudiofile -lm -lmalloc
-
- clean:
- rm -f *.o
-
- clobber: clean
- rm -f $(ALL)
-
- *********************************************************************** */
- #include <stdio.h>
- #include <math.h>
- #include "audio.h"
- #include "audiofile.h"
- #include <sys/schedctl.h>
-
- /* play module parameters */
- typedef struct oscillatordata {
- int loopType; /* off, forward, backward, forward&backward */
-
- int goingForward; /* current loop direction */
- int variSpeed; /* TRUE if sample is to be played at rate other
- than original sampling rate */
-
- short *sampleBase; /* ptr to audio data */
- int sampleLength; /* length of audio data */
- int sampleDone;
-
- short *outputBuffer; /* ptr to oscillator output buffer */
- int outputBufferLength; /* length of oscillator output buffer */
-
- int playSample; /* current sample to write into output buffer */
- double playSampleDouble; /* ditto, but for varispeed */
- double playIncrementDouble; /* distance to next sample */
- } OscillatorData;
-
- /* loop directions */
- #define LOOP_OFF 1
- #define LOOP_FORWARD 2
- #define LOOP_BACKWARD 3
- #define LOOP_FORWARD_N_BACKWARD 4
-
- #define FALSE 0
- #define TRUE 1
-
- /* some Audio Library stuff */
- ALconfig aconfig;
- ALport outport;
-
- /* some Audio File library stuf */
- AFfilesetup newSetUp;
- AFfilehandle sourceFile;
- int writeToAudioFile = FALSE;
- char sourceFileName[2000];
-
- /* execution time = numBlocks*OUTPUT_BLOCK_SIZE/sampling rate
- Example:
- 668*2048/44100 = ~31 seconds
- */
- #define NUM_BLOCKS 668
- #define OUTPUT_BLOCK_SIZE 2048
- #define SAMPLING_RATE 44100
- double samplingRate = (double)SAMPLING_RATE;
- int numBlocks = NUM_BLOCKS;
-
- #define MAX_OSCILLATORS 32
- /* don't set NUM_OSCILLATORS < 4 oscillators */
- #define NUM_OSCILLATORS 16
- #define NUM_WAVES 2000
- int pitchSlideOn = 1; /* enables pitch slide */
- double pitchInHertz = 55.0; /* initial pitch of wave, subject to pitch slide */
-
- static char someWavesUsage[]
- =
- "\t\t-help\n \
- \t\t-time (seconds > 0) default: 10\n\
- \t\t<output file name>";
-
- /*
- * SpecifyLoopType: set loop to off, forward, backward or forward/backward
- *
- * ---------------------------------------------------------------------- */
- void
- SpecifyLoopType(OscillatorData *data, int loopType)
- {
- switch (loopType)
- {
- case LOOP_OFF:
- data->loopType = loopType;
- break;
- case LOOP_FORWARD:
- data->loopType = loopType;
- data->goingForward = TRUE;
- break;
- case LOOP_BACKWARD:
- data->loopType = loopType;
- data->goingForward = FALSE;
- break;
- case LOOP_FORWARD_N_BACKWARD:
- data->loopType = loopType;
- break;
- default:
- fprintf(stderr, "SpecifyLoopType(): bogus loop type %d.\n", loopType);
- }
- } /* ----------------------- end SpecifyLoopType() --------------------- */
-
- /*
- * ResetOscillator: reset oscillator to play from beginning.
- *
- * ---------------------------------------------------------------------- */
- void
- ResetOscillator(OscillatorData *data)
- {
- if (data != NULL)
- {
- /* start at beginning of sample */
- data->playSample = 0;
- data->playSampleDouble = 0.0;
- data->sampleDone = FALSE;
- data->goingForward = TRUE;
- }
- } /* ----------------------- end ResetOscillator() --------------------- */
-
- /*
- * InitAL: create,initialize an AL output port.
- * -------------------------------------------------------------------- */
- void
- InitAL()
- {
- long pvbuf[2];
-
- /* get new ALconfiguration */
- aconfig = ALnewconfig();
- /* fill new ALconfiguration */
- ALsetqueuesize(aconfig, SAMPLING_RATE); /* one second at selected sampling rate */
- ALsetwidth(aconfig, AL_SAMPLE_16);
- ALsetchannels(aconfig, AL_STEREO);
-
- /* open a new ALport with the ALconfiguration */
- outport = ALopenport("xxx", "w", aconfig);
- if ((outport == (ALport)0) || (outport == (ALport)-1))
- {
- fprintf(stderr,"InitAL(): failed to open audio write port\n");
- return;
- }
-
- /* set hardware sampling rate */
- pvbuf[0] = AL_OUTPUT_RATE;
- pvbuf[1] = SAMPLING_RATE,
- ALsetparams(AL_DEFAULT_DEVICE, pvbuf, 2);
- } /* ----------------------- end InitAL() --------------------- */
-
- /*
- * CreateOscillator: create and initialize oscillator data structure
- * -------------------------------------------------------------------- */
- OscillatorData *
- CreateOscillator()
- {
- int i;
- OscillatorData *tmpData;
-
- /* set up play back structure */
- tmpData = (OscillatorData *) malloc(sizeof(OscillatorData));
- if (tmpData != NULL)
- {
- /* allocate oscillator output buffer */
- tmpData->outputBufferLength = OUTPUT_BLOCK_SIZE;
- tmpData->outputBuffer = (short *) malloc(OUTPUT_BLOCK_SIZE*sizeof(short));
- if (tmpData->outputBuffer == NULL)
- {
- free(tmpData);
- return (NULL);
- }
-
- ResetOscillator(tmpData);
- tmpData->playIncrementDouble = 1.0; /* playback at original sampling rate */
- tmpData->variSpeed = TRUE; /* assume playback ratio will change later */
- SpecifyLoopType(tmpData, LOOP_FORWARD); /* loop forward */
- }
- else
- return (NULL);
-
- return (tmpData);
- } /* ----------------------- end CreateOscillator() --------------------- */
-
- /*
- * RunOscillator: play data with loops.
- * Assume one data buffer of type short. includes sampling
- * rate conversion via truncated fractional counter.
- * This is among the least effective of sampling rate converters.
- * However, the method requires minimum compute power.
- *
- * With a double precision counter, frequency
- * precision decreases with length of the audio sample.
- * Practically, it's not a problem, since a sample
- * with a 31-bit length takes 12 hours to play (at 44100
- * samples/second), yielding a frequency precision
- * of 21-bits.
- *
- * This routine loops the ENTIRE sample. Code here is a
- * simple example.
- *
- * ---------------------------------------------------------------------- */
- void
- RunOscillator(OscillatorData *data)
- {
- register long i;
- register short *output; /* ptr to output buffer */
- register short *sampleBase;
- register int end, index, goingForward, playSample;
- register double playSampleDouble, playIncrementDouble;
-
- if (data != NULL)
- {
- /* load parameters */
- sampleBase = data->sampleBase;
- end = data->sampleLength;
-
- /*
- * ------------------------------ perform play
- */
- output = data->outputBuffer;
- i = data->outputBufferLength+1;
- /*
- * compute output at sampling rate not equal to original
- */
- if (data->variSpeed == TRUE)
- {
- playSampleDouble = data->playSampleDouble;
- playIncrementDouble = data->playIncrementDouble;
- switch (data->loopType)
- {
- case LOOP_OFF:
- if (data->sampleDone == FALSE)
- {
- for (; --i > 0; output++)
- {
- index = (int) playSampleDouble;
- if (index >= end)
- break;
- output[0] = sampleBase[index];
- playSampleDouble += playIncrementDouble;
- }
- if (index >= end)
- data->sampleDone = TRUE;
- }
-
- /* sample is done: output zero-valued samples to rest of buffer */
- for (; --i > 0; output++)
- output[0] = 0.0;
- break;
-
- case LOOP_FORWARD:
- index = (int) playSampleDouble;
- for (; --i > 0; output++)
- {
- output[0] = sampleBase[index];
-
- playSampleDouble += playIncrementDouble;
- index = (int) playSampleDouble;
- if (index >= end)
- {
- index = 0;
- playSampleDouble -= (double) end;
- }
- }
- break;
-
- case LOOP_BACKWARD:
- index = (int) playSampleDouble;
- for (; --i > 0; output++)
- {
- output[0] = sampleBase[index];
- playSampleDouble -= playIncrementDouble;
- if (index < 0)
- {
- index = end;
- playSampleDouble += (double) end;
- }
- }
- break;
-
- case LOOP_FORWARD_N_BACKWARD:
- goingForward = data->goingForward;
- index = (int) playSampleDouble;
- for (; --i > 0; output++)
- {
- /* do forward thang */
- if (goingForward == TRUE)
- {
- output[0] = sampleBase[index];
- playSampleDouble += playIncrementDouble;
- index = (int) playSampleDouble;
-
- /* careful about mirror-around points */
- if (index >= end)
- {
- playSampleDouble = ((double) (end + end)) - playSampleDouble;
- goingForward = FALSE;
- }
- }
- /* do backward thang */
- else
- {
- output[0] = sampleBase[index];
- playSampleDouble -= playIncrementDouble;
- index = (int) playSampleDouble;
-
- /* careful about mirror-around points */
- if (index < 0)
- {
- playSampleDouble = -playSampleDouble;
- goingForward = TRUE;
- }
- }
- }
- /* save loop direction status */
- data->goingForward = goingForward;
- break;
-
- default:
- fprintf(stderr,"RunOscillator(): bogus loopType %d\n", data->loopType);
- return;
- }
- /* save parameters */
- data->playSampleDouble = playSampleDouble;
- }
- /*
- * compute output at sampling rate equal to original. This requires less CPU time.
- */
- else
- {
- playSample = data->playSample;
- switch (data->loopType)
- {
- case LOOP_OFF:
- if (data->sampleDone == FALSE)
- {
- for (; (playSample < end)&&(--i > 0); output++)
- {
- output[0] = sampleBase[playSample++];
- }
- if (playSample >= end)
- data->sampleDone = TRUE;
- }
-
- /* now output zero-valued samples */
- for (; --i > 0; output++)
- {
- output[0] = 0.0;
- }
- break;
-
- case LOOP_FORWARD:
- for (; --i > 0; output++)
- {
- output[0] = sampleBase[playSample++];
-
- if (playSample >= end)
- playSample = 0;
- }
- break;
-
- case LOOP_BACKWARD:
- for (; --i > 0; output++)
- {
- output[0] = sampleBase[playSample--];
-
- if (playSample < 0)
- playSample = end;
- }
- break;
-
- case LOOP_FORWARD_N_BACKWARD:
- goingForward = data->goingForward;
- for (; --i > 0; output++)
- {
- if (goingForward == TRUE)
- {
- output[0] = sampleBase[playSample++];
-
- if (playSample >= end)
- goingForward = FALSE;
- }
- else
- {
- output[0] = sampleBase[--playSample];
-
- if (playSample <= 0)
- goingForward = TRUE;
- }
- }
- /* save loop direction status */
- data->goingForward = goingForward;
- break;
-
- default:
- fprintf(stderr,"RunOscillator(): bogus loopType %d\n", data->loopType);
- return;
- }
- /* save parameters */
- data->playSample = playSample;
- }
- }
- } /* ----------------------- end RunOscillator() --------------------- */
-
- /*
- * MixOutputBuffers: mix specified number of output buffers in equal
- * proportion.
- * -------------------------------------------------------------------- */
- void
- MixOutputBuffers(OscillatorData *data[], short *finalMix, int totalBuffers)
- {
- int i, j;
- int mix; /* 32 bit mixer to avoid intermediate overflow during mix */
- short *finalMixPtr;
-
- /*
- * mix an arbitrary # of buffers. This routine is GENERAL. You will gain
- * more performance by coding for the # of buffers you need.
- */
- finalMixPtr = finalMix;
- for (i = 0; i < OUTPUT_BLOCK_SIZE; i++)
- {
- mix = 0;
- for (j = 0; j < totalBuffers; j += 2)
- {
- mix += (int) data[j]->outputBuffer[i];
- }
- /* grossly scale output such that no overflow occurs */
- /* alternative: saturation. */
- *finalMixPtr++ = (short) (mix/(totalBuffers/2));
- }
- } /* ----------------------- end MixOutputBuffers() --------------------- */
-
- /*
- * SetPlaybackRatio: specify ratio: playback rate/original sampling rate
- * -------------------------------------------------------------------- */
- void
- SetPlaybackRatio(OscillatorData *data, double ratio)
- {
- if (data != NULL)
- data->playIncrementDouble = ratio;
- } /* ----------------------- end SetPlaybackRatio() --------------------- */
-
- /*
- * SetPlaybackEqualTemperedRatio: specify ratio: playback rate/original sampling
- * rate in equal-tempered units of half steps and cents.
- * -------------------------------------------------------------------- */
- void
- SetPlaybackEqualTemperedRatio(OscillatorData *data, int halfSteps, int cents)
- {
- double pitchChange;
-
- /* a half step is the difference in pitch between two adjacent notes on, for
- example, a piano. A cent is 1/100 of a half step. This function is
- pricey for pitch computing. Might be a better idea to store a collection
- of these values in a table. */
- if (data != NULL)
- {
- /* add half steps and cents and compute 2^(frequency/12) */
- pitchChange = ((double) halfSteps) + (((double) cents)/100.0);
- data->playIncrementDouble = pow(2.0, pitchChange/12.0);
- }
- } /* ----------------------- end SetPlaybackEqualTemperedRatio() --------------------- */
-
- /*
- * GenerateSawtoothWave: generate non-bandlimited sawtooth wave.
- * -------------------------------------------------------------------- */
- void
- GenerateSawtoothWave(short *waveMemory, int length)
- {
- int i;
- double arg, argInc;
-
- /*
- * compute sawtooth wave. This process generates a wave with all harmonics
- * with amplitude proportional to 1/harmonic #.
- */
- arg = 32767.0;
- argInc = 32767.0/((double) length);
- for (i = 0; i < length; i++, arg -= argInc)
- waveMemory[i] = (short) arg;
- } /* ----------------------- end GenerateSawtoothWave() --------------------- */
-
- /*
- * GenerateTriangleWave: generate non-bandlimited triangle wave.
- * -------------------------------------------------------------------- */
- void
- GenerateTriangleWave(short *waveMemory, int length)
- {
- int i;
- double arg, argInc;
-
- /*
- * compute triangle wave. This process generates a wave with odd harmonics
- * with amplitude proportional to 1/(harmonic # squared).
- */
- arg = -32767.0;
- argInc = 4.0*32767.0/((double) length);
- /* do positive slope portion */
- for (i = 0; (i < length)&&(arg <= 32767.0); i++, arg += argInc)
- waveMemory[i] = (short) arg;
-
- /* do negative slope portion */
- arg = 2.0*32767.0 - arg; /* mirror xtra back into range */
- for (; i < length; i++, arg -= argInc)
- waveMemory[i] = (short) arg;
- } /* ----------------------- end GenerateTriangleWave() --------------------- */
-
- /*
- * GeneratePulseWave: generate non-bandlimited pulse wave of specified duty cycle.
- * -------------------------------------------------------------------- */
- void
- GeneratePulseWave(short *waveMemory, int length, int dutyCycleEnd)
- {
- int i;
-
- /* check for valid duty cycle Range: 2..length-2 */
- if ((dutyCycleEnd <= 1)||(dutyCycleEnd >= length-1))
- {
- fprintf(stderr, "GeneratePulse(): bogus duty cycle end %d. No wave, dude\n", dutyCycleEnd);
- return;
- }
-
- /* Here, duty cycle is percentage of wavelength signal positive.
- * Duty cycle specifies the harmonic relationships. A pulse function in the
- * time domain transforms into a sin(x)/x or sinc(x) function in the
- * frequency domain. The value of the sinc function will determine the
- * amplitude of the harmonics.
- *
- * A square wave (duty cycle 50%) is member of the pulse wave family. Just
- * so happens that the frequency domain sinc function for the square wave
- * is zero at every even harmonic. Thus, a square wave contains odd only
- * harmonics whose amplitudes are 1/harmonic #.
- *
- * A pulse wave with a 33% duty cycle is missing every third harmonic.
- * A pulse wave with a 25% duty cycle is missing every fourth harmonic.
- * And so on, and so on. However, the present harmonics don't have a
- * simple 1/harmonic # amplitude relationship. Need to examin the sinc function.
- * Very small and very large duty cycles have a nasal quality.
- *
- * A traditional method for producing ballsy sounds from a single oscillator
- * is to modulate the duty cycle. This can be done by computing 400 or so
- * pulse waves with increasing duty cycles and cycling through this list of
- * waves in a forward backward manner. THIS IS TERMED PULSE WIDTH MODULATION.
- * The more samples are in your wave, the thinner your thinnest pulse can be.
- *
- * The modulation rate is determined by the rate at which the pulse widths change.
- * This is determined by the rate at which the pulse wave set is stepped
- * thru and the number of different pulse waves in the set.
- */
-
- /* do duty cycle portion */
- for (i = 0; i < dutyCycleEnd; i++)
- waveMemory[i] = 32767;
-
- /* do off portion */
- for (; i < length; i++)
- waveMemory[i] = -32767;
- } /* ----------------------- end GeneratePulseWave() --------------------- */
-
- /*
- * GenerateWave: generate general wave as sum of of cosines of specified
- * length.
- * -------------------------------------------------------------------- */
- void
- GenerateWave(short *waveMemory, int length)
- {
- int i, j;
- double arg, argInc;
- int someNumber;
- int numberOfHarmonics = 99;
- double amplitude[500];
- int phase[500];
- double *cosineTable;
- int cosineIndex, cosineIncrement;
- double maxValue, someDouble;
- double *wave;
-
- /* allocate memory for cosine table, intermediate storage and final product */
- cosineTable = (double *) malloc(length*sizeof(double));
- if (cosineTable == NULL)
- {
- fprintf(stderr,"GenerateWave(): failed to allocate cosineTable. No wave, dude.\n");
- return;
- }
- wave = (double *) malloc(length*sizeof(double));
- if (wave == NULL)
- {
- fprintf(stderr,"GenerateWave(): failed to allocate intermediate memory. No wave, dude.\n");
- free(cosineTable);
- return;
- }
-
- /* generate a full cycle of a cosine table */
- argInc = (8.0*atan(1.0))/((double) (length)); /* 2*PI/wavelength */
- arg = 0.0;
- for (i = 0; i < length; i++, arg += argInc)
- cosineTable[i] = cos(arg);
-
- /* initialize harmonic amplitude array. */
- for (i = 1; i <= numberOfHarmonics; i+=5)
- {
- amplitude[i] = 1.0/((double) i);
- amplitude[i+1] = 0.0;
- amplitude[i+2] = 0.0;
- amplitude[i+3] = 0.0;
- amplitude[i+4] = 0.0;
- }
-
- /* initialize harmonic phase array. Phase must be in range
- 0..length-1 */
- for (i = 1; i <= numberOfHarmonics; i++)
- phase[i] = 0;
-
- /* clear wave memory */
- for (i = 0; i < length; i++)
- wave[i] = 0.0;
-
- /* generate wave with specified # harmonics */
- for (i = 1; i <= numberOfHarmonics; i++)
- {
- if (amplitude[i] != 0.0)
- {
- cosineIncrement = i;
- /*cosineIndex = phase[i];*/
- cosineIndex = 0;
- for (j = 0; j < length; j++)
- {
- wave[j] += amplitude[i]*cosineTable[cosineIndex];
- cosineIndex += cosineIncrement;
- /* bound cosine table index to actual table */
- if (cosineIndex >= length)
- cosineIndex -= length;
- }
- }
- }
-
- /* find largest value in wave */
- maxValue = 0.0;
- for (i = 0; i < length; i++)
- {
- someDouble = wave[i];
- if (someDouble < 0.0)
- someDouble = -someDouble;
- if (someDouble > maxValue)
- maxValue = someDouble;
- }
-
- /* write into sample memory. Product is fit in range -32768..32767 */
- maxValue = 32767.0/maxValue; /* normalization factor */
- for (i = 0; i < length; i++)
- waveMemory[i] = (short) (maxValue*wave[i]);
-
- free(cosineTable);
- free(wave);
- } /* ----------------------- end GenerateWave() --------------------- */
-
- /*
- * InterleaveShorts: interleave two buffers (of equal length) of shorts
- * into a single buffer of shorts.
- * -------------------------------------------------------------------- */
- void
- InterleaveShorts(short *inBufferLeft, short *inBufferRight, short *outBuffer, int inBufferLength)
- {
- register int i;
- register short *inLeftPtr, *inRightPtr, *outPtr;
-
- inLeftPtr = inBufferLeft;
- inRightPtr = inBufferRight;
- outPtr = outBuffer;
- /* output buffer shold not be either of input buffers */
- for (i = inBufferLength+1; --i > 0; inLeftPtr++, inRightPtr++, outPtr+=2)
- {
- outPtr[0] = inLeftPtr[0];
- outPtr[1] = inRightPtr[0];
- }
- } /* ----------------------- end InterleaveShorts() --------------------- */
-
- /*
- * DestroyOscillator: destroy oscillator data structure
- * -------------------------------------------------------------------- */
- void
- DestroyOscillator(OscillatorData *data)
- {
- if (data != NULL)
- {
- /* free sound memory */
- if (data->sampleBase != NULL)
- free(data->sampleBase);
- free(data);
- }
- } /* ----------------------- end DestroyOscillator() --------------------- */
-
- /* **********************************************************************
- * ParseCommandLine: parse input command line for user options
- * ********************************************************************** */
- static void
- ParseCommandLine(int argc, char **argv)
- {
- int i;
- char *s;
- double outputTime;
-
- for (i = 1; i < argc; i++)
- {
- /* check for -t or -time argument */
- if (!strncmp(argv[i], "-t", sizeof("-t")-1))
- {
- s = argv[++i];
- if (!isdigit(s[0]))
- {
- fprintf(stderr, "Usage: %s\n", someWavesUsage);
- exit(1);
- }
- outputTime = atof(s);
- if (outputTime <= 0.0)
- exit(1);
- /* compute#blocks of OUTPUT_BLOCK_SIZE to be synthesized */
- numBlocks = (0.5+outputTime*samplingRate/((double) OUTPUT_BLOCK_SIZE));
- }
- /* check for -h or -help */
- else if (!strncmp(argv[i], "-h", sizeof("-h")-1))
- {
- fprintf(stderr, "Usage: %s\n", someWavesUsage);
- exit(1);
- }
- /* ah, must be a file name */
- else if (argv[i][0] != '\0')
- {
- strcpy(sourceFileName, argv[i]);
- writeToAudioFile = TRUE;
- }
- }
- } /* ----------------------- end ParseCommandLine() --------------------- */
-
- /*
- * main: crank out some cool sounds
- * -------------------------------------------------------------------- */
- main(int argc, char **argv)
- {
- int i, j;
- OscillatorData **someData;
- short *mixLeft, *mixRight, *finalMix;
- short *waveMemory;
- int waveLength;
- int pulseIndex, pulseWidth, pulseIncrement, numPulseWaves;
- int pulseDataEnd;
- int stereoSpread;
- int done;
- double pitchMultiplier;
- int blockCount;
- int mainPid;
-
- /* sampling rate / frequency of resultant waveform */
- waveLength = samplingRate/(2.0*pitchInHertz);
-
- /* execution time = numBlocks*OUTPUT_BLOCK_SIZE/sampling rate
- Example:
- 668*2048/44100 = ~31 seconds
- */
- numBlocks = (0.5+10.0*samplingRate/((double) OUTPUT_BLOCK_SIZE));
- ParseCommandLine(argc, argv);
-
- /*
- * initialize setup structure for new file
- */
- if (writeToAudioFile == TRUE)
- {
- newSetUp = AFnewfilesetup();
- AFinitchannels(newSetUp, AF_DEFAULT_TRACK, 2);
- AFinitrate(newSetUp, AF_DEFAULT_TRACK, samplingRate);
-
- /* open new file */
- sourceFile = AFopenfile(sourceFileName, "w", newSetUp);
- if (sourceFile == AF_NULL_FILEHANDLE)
- {
- fprintf(stderr, "couldn't open %s\n", sourceFileName);
- exit(1);
- }
- }
- /* make main process of highest priority normal process, non-degrading priority */
- mainPid = getpid();
- schedctl(NDPRI, mainPid, NDPNORMMAX);
-
- /*
- * allocate some memory
- */
- someData = (OscillatorData **) malloc(MAX_OSCILLATORS*sizeof(OscillatorData));
- mixLeft = (short *) malloc(OUTPUT_BLOCK_SIZE*sizeof(short));
- mixRight = (short *) malloc(OUTPUT_BLOCK_SIZE*sizeof(short));
- finalMix = (short *) malloc(2*OUTPUT_BLOCK_SIZE*sizeof(short));
- waveMemory = (short *) malloc(NUM_WAVES*waveLength*sizeof(short));
- if ((someData == NULL)||(mixLeft == NULL)||(mixRight == NULL)||
- (finalMix == NULL)||(waveMemory == NULL))
- {
- fprintf(stderr, "main(): no memory available. Exiting. \n");
- exit (-1);
- }
- /*
- * choose from this plethora of wave generation functions
- */
- #ifdef SAWTOOTH_WAVE
- GenerateSawtoothWave(waveMemory, waveLength);
- #endif
- #ifdef PULSE_WAVE
- GeneratePulseWave(waveMemory, waveLength, 10);
- #endif
- #ifdef SQUARE_WAVE
- GeneratePulseWave(waveMemory, waveLength, waveLength/2);
- #endif
- #ifdef COOL_WAVE
- GenerateWave(waveMemory, waveLength);
- #endif
- #ifdef TRIANGLE_WAVE
- GenerateTriangleWave(waveMemory, waveLength);
- #endif
-
- /*
- * generate collection of pulse waves for pulse width modulation.
- */
- /* Keep track of number of pulse waves actually generated. Most extreme
- pulse widths skipped because they are DC. # of waves generated depends
- on variable waveLength */
- /* ALSO TRY GENERATING OTHER SETS OF RELATED WAVES USING GenerateWave() */
- #ifdef PULSE_WIDTH_MODULATION
- numPulseWaves = 0;
- for (i = 0, pulseWidth=2; pulseWidth < (waveLength-1); pulseWidth++, i++)
- {
- GeneratePulseWave(&waveMemory[i*waveLength], waveLength, pulseWidth);
- numPulseWaves++;
- }
- /* init pulse width parameters */
- pulseIndex = 0*waveLength; /* start at duty cycle 2 */
- pulseIncrement = waveLength;
- pulseDataEnd = (numPulseWaves-1)*waveLength;
- #endif
-
- /* set up audio port */
- InitAL();
-
- /*
- * --------------------------- create oscillators
- */
- for (i = 0; i < MAX_OSCILLATORS; i++)
- {
- someData[i] = CreateOscillator();
- if (someData[i] == NULL)
- {
- fprintf(stderr, "main(): failed to create voice %d. Exiting. \n");
- exit (-1);
- }
- /* tell oscillator where to get wave data. Specify length of data. Could
- also load data from a file */
- someData[i]->sampleLength = waveLength;
- someData[i]->sampleBase = waveMemory;
-
- /* specify loop type. Have choice of LOOP_OFF, LOOP_FORWARD, LOOP_BACKWARD,
- LOOP_FORWARD_N_BACKWARD */
- SpecifyLoopType(someData[i], LOOP_FORWARD);
-
- /* turn varispeed on if playback rates will differ from original sampling rate */
- someData[i]->variSpeed = TRUE;
-
- /* tell oscillator to start playing at beginning of wave */
- ResetOscillator(someData[i]);
- }
-
- /*
- * 8 oscillators are used to play each waveform. Two oscillators
- * playing the same audio at two slightly different rates (different tunings)
- * produce amplitude "beating" among the harmonics.
- * For waves with a rich harmonic spectrum, this beating yields a fatter
- * sound. This difference in playback rate is termed detuning. More detuned
- * oscillators yield a fatter sound.
- *
- * In example below, an A=55 Hz waveform is played back with two
- * groups of 4 oscillators. The first plays at A=55/2 (-12 semitones)
- * and the second plays at A=55/4 (-24 semitones). So a total of
- * eight oscillators play in the left channel and the right channel.
- *
- * Stereo is generated by playing the right channel a little faster than
- * the left channel. This method for stereo generation adds well in mono.
- *
- * Also note that the playback rate actually changes the time envelope and
- * the pitches. Slower playback rates (< 1.0 or < 0 semitones) slow down
- * the time evolution of the spectrum and pitch shift the spectrum down.
- * Faster playback rates (> 1.0 or > 0 semitones) speed up
- * the time evolution of the spectrum and pitch shift the spectrum up.
- *
- * Changes in playback rate using the suppiled pitch change method will generally cause
- * alising/imaging artifacts for large (> a few semitones) changes in the playback
- * rate. The method is simple. More complex methods would reduce polyphony.
- *
- * On IRIS Indigo:
- * At 44100 samples/second output rate, OPTIMIZER=02:
- * independent waveforms, indpendent loop type, independent playback rate
- * can do 21, mixed oscillators
- * independent waveforms, indpendent loop type, no change in playback rate
- * can do 26, mixed oscillators
- *
- */
- /*
- * specify playback ratio of data
- */
- stereoSpread = 13; /* playback rate difference between channels */
- /* left channel, higher note */
- SetPlaybackEqualTemperedRatio(someData[0], -12, 0);
- SetPlaybackEqualTemperedRatio(someData[2], -12, 9);
- SetPlaybackEqualTemperedRatio(someData[4], -12, 18);
- SetPlaybackEqualTemperedRatio(someData[6], -12, 27);
- /* right channel, higher note */
- SetPlaybackEqualTemperedRatio(someData[1], -12, 0+stereoSpread);
- SetPlaybackEqualTemperedRatio(someData[3], -12, 9+stereoSpread);
- SetPlaybackEqualTemperedRatio(someData[5], -12, 18+stereoSpread);
- SetPlaybackEqualTemperedRatio(someData[7], -12, 27+stereoSpread);
-
- /* left channel, lower note */
- SetPlaybackEqualTemperedRatio(someData[8], -24, 0);
- SetPlaybackEqualTemperedRatio(someData[10], -24, 9);
- SetPlaybackEqualTemperedRatio(someData[12], -24, 18);
- SetPlaybackEqualTemperedRatio(someData[14], -24, 27);
- /* right channel, lower note */
- SetPlaybackEqualTemperedRatio(someData[9], -24, 0+stereoSpread);
- SetPlaybackEqualTemperedRatio(someData[11], -24, 9+stereoSpread);
- SetPlaybackEqualTemperedRatio(someData[13], -24, 18+stereoSpread);
- SetPlaybackEqualTemperedRatio(someData[15], -24, 27+stereoSpread);
-
- /*
- * ------ generate numBlocks chunks of OUTPUT_BLOCK_SIZE
- */
- for (blockCount = 0; blockCount < numBlocks; blockCount++)
- {
- /* compute NUM_OSCILLATORS. */
- for (j = 0; j < NUM_OSCILLATORS; j++)
- if (someData[j] != NULL)
- RunOscillator(someData[j]);
-
- /* mix oscillators with equal amplitudes */
- MixOutputBuffers(someData, mixLeft, NUM_OSCILLATORS/2);
- MixOutputBuffers(&someData[1], mixRight, NUM_OSCILLATORS/2);
-
- /* interleave stereo data */
- InterleaveShorts(mixLeft, mixRight, finalMix, OUTPUT_BLOCK_SIZE);
-
- /* write data to AL audio port */
- ALwritesamps(outport, finalMix, 2*OUTPUT_BLOCK_SIZE);
-
- /* write to file */
- if (writeToAudioFile == TRUE)
- AFwriteframes(sourceFile, AF_DEFAULT_TRACK, finalMix, OUTPUT_BLOCK_SIZE);
-
- /*
- * Enabling pitch slide will cause the playback rate to increase
- * exponentially. Pitch is exponentially related to the playback rate.
- * This code has no bounds onaliasing, since that depends on the spectrum
- * of your waveform.
- * For pitch to increase, pitchMultiplier > 1
- * For pitch to decrease, pitchMultiplier < 1
- */
- if (pitchSlideOn == TRUE)
- {
- pitchMultiplier = 1.001;
- for (j = 0; j < NUM_OSCILLATORS; j++)
- {
- someData[j]->playIncrementDouble *= pitchMultiplier;
- /* bound pitch increase */
- if (someData[j]->playIncrementDouble < 0.0)
- someData[j]->playIncrementDouble = 0.0;
- else if (someData[j]->playIncrementDouble > 5.0)
- someData[j]->playIncrementDouble = 5.0;
- }
- }
-
- /*
- * do pulse width modulation: this code changes the waves for all oscillators
- * using the aforegenerated collection of pulse waves. Listen to to waveform
- * change from thin/nasal to rich. You can also hear the first few harmonics
- * fade in and out. Probably only 3rd and 5th, since 2nd and 4th are covered
- * up since the notes are played in an interval of an octave.
- * Be sure that you don't forget to enable the generation of the pulse waves,
- * earlier in main().
- */
- #ifdef PULSE_WIDTH_MODULATION
- pulseIndex += pulseIncrement;
- #ifdef PULSE_WAVE
- if ((pulseIndex < 0)||(pulseIndex >= pulseDataEnd))
- {
- pulseIncrement = -pulseIncrement;
- if (pulseIndex < 0)
- pulseIndex = -pulseIndex;
- else if (pulseIndex >= pulseDataEnd)
- pulseIndex = 2*pulseDataEnd - pulseIndex;
- }
- for (j = 0; j < NUM_OSCILLATORS; j++)
- someData[j]->sampleBase = &waveMemory[pulseIndex];
- #endif
- #endif
- }
-
- /* close audio file */
- if (writeToAudioFile == TRUE)
- AFclosefile(sourceFile);
-
- /* wait until AL sample queue empties */
- while (ALgetfilled(outport) > 0)
- sginap(10);
-
- /* when all is done, close AL port and free AL configuration */
- ALcloseport(outport);
- ALfreeconfig(aconfig);
- } /* ----------------------- end main() --------------------- */
-
-
-